package com.udf.AopShow.Aspect;

import com.udf.AopShow.UndiDataFilter;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import sun.reflect.misc.MethodUtil;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by 张未然 on 2015/9/13.
 * 参考http://my.oschina.net/itblog/blog/211693
 */
@Aspect
@Component
@Order(100)//优先执行缓存里的方法，如果找不到再执行这个。
public class PermissionAspect {

    @Pointcut("execution(@com.udf.AopShow.UndiDataFilter * *(..))")
    public void hasDFAnnotationMethod(){};

    @Pointcut("execution(* org.springframework.data.jpa.repository.JpaSpecificationExecutor+.*(..))")
    public void isJpaSpecificationExecutorSubclass(){}

    @Pointcut("@annotation(com.udf.AopShow.UndiDataFilter)")
    public void hasDataFilterAnnotationMethod(){};

    @Pointcut("@annotation(com.udf.AopShow.UndiDataFilter)")
    public void hasDataFilterAnnotationType(){};

    @Around("hasDataFilterAnnotationType()")
    public Object permissionFilter(ProceedingJoinPoint pjp){
        Object result = null;
        MethodSignature m = (MethodSignature) pjp.getSignature();
        Object[] args = pjp.getArgs();
        Class<?>[] parameterClass = parseObjtoCls(args);
        Method actualMethod = null;
        try {
            actualMethod = pjp.getTarget().getClass().getMethod(m.getMethod().getName(),parameterClass);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        UndiDataFilter dataFilter  = AnnotationUtils.getAnnotation(actualMethod, UndiDataFilter.class);
        String resourceType = dataFilter.resourceType();
        Specification permissionSpec = buildSpecificiation(resourceType);

        List<Object> argsList = new ArrayList<>();
        if (args != null && args.length > 0) {
            argsList.addAll(Arrays.asList(args));
            Object lastArgument = argsList.get(args.length - 1);
            if(lastArgument instanceof Specification){
                //已存在spec 拼合
                Specification oriSpec = (Specification) lastArgument;
                Specification newSpec = Specifications.where(oriSpec).and(permissionSpec);
                argsList.set(args.length - 1,newSpec);
            }else{
                argsList.add(permissionSpec);
            }
        }else{
           argsList.add(permissionSpec);
        }

        try {
            result = invokeOverLoadingMethod(m, pjp.getTarget(), argsList);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        return result;
    }

    private Class<?>[] parseObjtoCls( Object[] objs){
        Class<?>[] argsClasses = new Class<?>[objs.length];
        for (int i=0;i<objs.length;i++) {
            Class<?> argClass =objs[i].getClass();
            if(argClass.isAnonymousClass()){
                Class<?>[] interfaces = argClass.getInterfaces();
                if(interfaces!=null&&interfaces.length>0){
                    for (Class<?> anInterface : interfaces) {
                        if(anInterface.isAssignableFrom(Specification.class)){
                            argsClasses[i] = Specification.class;
                            continue;
                        }
                    }
                }else{
                    argsClasses[i] = argClass.getEnclosingClass();
                }
            }else{
                argsClasses[i] = argClass;
            }

        }
        return argsClasses;
    }

    private Object invokeOverLoadingMethod(MethodSignature oriMethodSignature,Object target,List argsList) throws Exception {
        Class<?>[] argsClasses = parseObjtoCls(argsList.toArray());
        try {
            System.out.println("PermissionAspect实际调用方法："+target.getClass()+"."+oriMethodSignature.getMethod().getName()+ " 参数："+argsList.toArray());
            return target.getClass().getMethod(oriMethodSignature.getMethod().getName(), argsClasses).invoke(target,argsList.toArray());

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new Exception("重载方法未找到，请检查"+ oriMethodSignature.getDeclaringType()+"是否加入了供重载的执行方法");
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new Exception("重载方法未找到，请检查"+ oriMethodSignature.getDeclaringType()+"是否加入了供重载的执行方法");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new Exception("重载方法未找到，请检查"+ oriMethodSignature.getDeclaringType()+"是否提供重载的执行方法");
        }
    }

    private Specification buildSpecificiation(String resourceType){
        return new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
                return cb.equal(root.get("belongs"),1);
            }
        };
    }
}
